﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;

using Sonic.Framework;
using Sonic.Framework.Site;

using NUnit.Framework;
using System.IO;

namespace Sonic.Framework.Tests.Site
{
    /// <summary>
    /// Summary description for Concatenation
    /// </summary>
    [TestFixture]
    public class ConcatenationTest
    {

        [Test]
        /// Test the result of concatenating two files together, checking the process output matches an expected file
        public void TestFileConcatenationResult()
        {
            Concatenation concat = new Concatenation();
            concat.Paths.Add(new SitePath(RelativizePath(@"~\ConcatFileTest\a.txt")));
            concat.Paths.Add(new SitePath(RelativizePath(@"~\ConcatFileTest\b.txt")));

            concat.OutputFilePath = new FileInfo(RelativizePath(@"~\ConcatFileTest\result.txt"));

            FileInfo result = concat.Concatenate();

            FileInfo expected = new FileInfo(RelativizePath(@"~\ConcatFileTest\expected.txt"));

            FileStream fsResult = null, fsExpect = null;

            try
            {
                fsResult = result.OpenRead();
                fsExpect = expected.OpenRead();

                // Are they the same FileStream?
                if (fsResult == fsExpect)
                {
                    Assert.Fail("FileStreams are the same");
                }

                // Are the expected and result the same size? They should be
                if (fsResult.Length != fsExpect.Length)
                {
                    Assert.Fail("Result and Expected are different file lengths.");
                }

                // Read bytes, ensuring they equal along the way
                int e, r;
                bool match = true;
                do
                {
                    e = fsExpect.ReadByte();
                    r = fsResult.ReadByte();
                    match = (e == r);
                    if (!match)
                    {
                        Assert.Fail("Bytes are not equal");
                        break;
                    }
                } while (match && (e != -1));
            }
            finally
            {
                // Clean up
                if (fsResult != null)
                {
                    fsResult.Close();
                    fsResult.Dispose();
                }
                if (fsExpect != null)
                {
                    fsExpect.Close();
                    fsExpect.Dispose();
                }
            }
        }

        [Test]
        /// Test the result of concatenating two files together, checking the process output matches an expected file
        /// and ensuring the inputs are deleted
        public void TestFileConcatenationResultAndSourceFileDeletion()
        {
            // Clone the files (we'll delete them later)
            FileInfo fileA = new FileInfo(RelativizePath(@"~\ConcatFileTest\a.txt"));
            FileInfo fileB = new FileInfo(RelativizePath(@"~\ConcatFileTest\b.txt"));
            //fileA.CopyTo(RelativizePath(@"~\ConcatFileTest\a-copy.txt"));
            //fileA.CopyTo(RelativizePath(@"~\ConcatFileTest\b-copy.txt"));
           
            // Do the concat
            Concatenation concat = new Concatenation();
            concat.Paths.Add(new SitePath(fileA));
            concat.Paths.Add(new SitePath(fileB));

            concat.OutputFilePath = new FileInfo(RelativizePath(@"~\ConcatFileTest\result.txt"));

            concat.DeleteSourceFiles = true;

            FileInfo result = concat.Concatenate();

            FileInfo expected = new FileInfo(RelativizePath(@"~\ConcatFileTest\expected.txt"));

            FileStream fsResult = null, fsExpect = null;

            try
            {
                fsResult = result.OpenRead();
                fsExpect = expected.OpenRead();

                // Are they the same FileStream?
                if (fsResult == fsExpect)
                {
                    Assert.Fail("FileStreams are the same");
                }

                // Are the expected and result the same size? They should be
                if (fsResult.Length != fsExpect.Length)
                {
                    Assert.Fail("Result and Expected are different file lengths.");
                }

                // Read bytes, ensuring they equal along the way
                int e, r;
                bool match = true;
                do
                {
                    e = fsExpect.ReadByte();
                    r = fsResult.ReadByte();
                    match = (e == r);
                    if(!match) {
                        Assert.Fail("Bytes are not equal");
                        break;
                    }
                } while (match && (e != -1));
            }
            finally
            {
                // Clean up
                if (fsResult != null)
                {
                    fsResult.Close();
                    fsResult.Dispose();
                }
                if (fsExpect != null)
                {
                    fsExpect.Close();
                    fsExpect.Dispose();
                }
            }

            // Check all input files have been deleted
            Assert.IsFalse(fileA.Exists);
            Assert.IsFalse(fileB.Exists);
        }

        #region File Path helpers
        // TODO: Consider moving these into external class, or extension to FileInfo

        /// <summary>
        /// Converts a path string to a relative path string. It replaces the tilder with the current execution directory.
        /// i.e. ~/file.exe --> C:\temp\file.exe
        /// </summary>
        /// <param name="path">Path to relativize</param>
        /// <returns>Full file system path. i.e. ~/file.exe --> C:\temp\file.exe</returns>
        private static string RelativizePath(string path)
        {
            if (path.StartsWith(@"/") || path.StartsWith(@"\"))
            {
                path = "~" + path;
            }

            if (path.StartsWith(@"~"))
            {
                string currentDirectory = System.IO.Directory.GetCurrentDirectory();
                path = CombinePaths(currentDirectory, path.Substring(1));
            }

            return path;
        }

        /// <summary>
        /// Converts a path string to a relative path string. It replaces the tilder with the root string variable
        /// i.e. ~/file.exe --> C:\temp\file.exe
        /// </summary>
        /// <param name="root">Root path</param>
        /// <param name="path">Path to relativize</param>
        /// <returns>Full file system path. i.e. ~\file.exe --> {root variable}\file.exe</returns>
        private static string RelativizePath(string root, string path)
        {
            if (path.StartsWith(@"/") || path.StartsWith(@"\"))
            {
                path = @"~" + path;
            }
            else if (path.StartsWith(@"~"))
            {
                path = path.Substring(1);
            }
            else
            {
                path = @"\" + path;
            }

            path = CombinePaths(root, path);

            return path;
        }

        /// <summary>
        /// A wrapper for System.IO.Path.Combine that makes it a bit more robust to preceeding slashes
        /// </summary>
        /// <param name="path1"></param>
        /// <param name="path2"></param>
        /// <returns>path1 combined with path2</returns>
        private static string CombinePaths(string path1, string path2)
        {
            if (path2[0] == '/' || path2[0] == '\\')
            {
                path2 = path2.Substring(1);
            }

            return System.IO.Path.Combine(path1, path2);
        }

        #endregion
    }

}
